/**
  ******************************************************************************
  * @file    py32f420_hal_flash_ex.c
  * @author  MCU Application Team
  * @brief   UART HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Universal Asynchronous Receiver Transmitter Peripheral (FLASH).
  *           + Initialization and de-initialization functions
  *           + IO operation functions
  *           + Peripheral Control functions
  *           + Peripheral State and Errors functions
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 Puya Semiconductor Co.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by Puya under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "py32f4xx_hal.h"
#include "py32f420_hal_flash_ex.h"

/** @addtogroup PY32F420_HAL_Driver
  * @{
  */

/** @defgroup FLASHEx FLASHEx
  * @brief FLASH Extended HAL module driver
  * @{
  */

#ifdef HAL_FLASH_MODULE_ENABLED

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/** @defgroup FLASHEx_Private_Functions FLASHEx Private Functions
  * @{
  */
static HAL_StatusTypeDef FLASH_OB_WRPConfig(uint32_t WRPArea);
static HAL_StatusTypeDef FLASH_OB_PCROPConfig(uint32_t PCROPConfig, uint32_t PCROPStartAddr, uint32_t PCROPEndAddr);
static HAL_StatusTypeDef FLASH_OB_SecMemConfig(uint32_t SECPROTConfig, uint32_t SecSize);
static uint32_t          FLASH_OB_GetWRP(void);
static uint32_t          FLASH_OB_GetRDP(void);
static uint32_t          FLASH_OB_GetUser(void);
static void              FLASH_OB_GetPCROP(uint32_t *PCROPConfig, uint32_t *PCROPStartAddr, uint32_t *PCROPEndAddr);
static void              FLASH_OB_OptrConfig(uint32_t UserType, uint32_t UserConfig, uint32_t RDPLevel);
static void              FLASH_OB_GetSecMem(uint32_t *SECConfig, uint32_t *SecUsePage);

/**
  * @}
  */

/* Exported functions -------------------------------------------------------*/
/** @defgroup FLASHEx_Exported_Functions FLASHEx Exported Functions
  * @{
  */

/** @defgroup FLASHEx_Exported_Functions_Group1 Extended IO operation functions
  * @brief   Extended IO operation functions
  *
@verbatim
 ===============================================================================
                ##### Extended programming operation functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to manage the Extended FLASH
    programming operations Operations.

@endverbatim
  * @{
  */
/**
  * @brief  Perform a mass erase or erase the specified FLASH memory pages.
  * @param[in]  pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
  *         contains the configuration information for the erasing.
  * @param[out]  PageError pointer to variable that contains the configuration
  *         information on faulty page in case of error (0xFFFFFFFF means that all
  *         the pages have been correctly erased).
  * @retval HAL_Status
  */
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
{
  HAL_StatusTypeDef status = HAL_OK;
  uint32_t address_index = 0;

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

    if ((pEraseInit->TypeErase & FLASH_TYPEERASE_MASSERASE) != 0)
    {
      /* Mass erase to be done */
      FLASH_MassErase(pEraseInit->Banks);

      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

      /* If the erase operation is completed, disable the MER Bit */
      CLEAR_BIT(FLASH->CR, FLASH_CR_MER);
    }
    else if(pEraseInit->TypeErase == FLASH_TYPEERASE_PAGEERASE)
    {
      /* Page Erase is requested */
      /* Check the parameters */
      assert_param(IS_FLASH_PROGRAM_ADDRESS(pEraseInit->PageAddress));
      assert_param(IS_FLASH_NB_PAGES(pEraseInit->PageAddress, pEraseInit->NbPages));

      /*Initialization of PageSectorError variable*/
      *PageError = 0xFFFFFFFFU;

      /* Erase page by page to be done*/
      for(address_index = pEraseInit->PageAddress;
          address_index < ((pEraseInit->NbPages * FLASH_PAGE_SIZE) + pEraseInit->PageAddress);
          address_index += FLASH_PAGE_SIZE)
      {
        FLASH_PageErase(address_index);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
        /* If the erase operation is completed, disable the PER Bit */
        CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
        if (status != HAL_OK)
        {
          /* In case of error, stop erase procedure and return the faulty address */
          *PageError = address_index;
          break;
        }
      }
    }
    else if(pEraseInit->TypeErase == FLASH_TYPEERASE_SECTORERASE)
    {
      /* Sector Erase is requested */
      /* Check the parameters */
      assert_param(IS_FLASH_PROGRAM_ADDRESS(pEraseInit->SectorAddress));
      assert_param(IS_FLASH_NB_SECTORS(pEraseInit->SectorAddress, pEraseInit->NbSectors));


      /*Initialization of PageSectorError variable*/
      *PageError = 0xFFFFFFFFU;

      /* Erase sector by sector to be done*/
      for(address_index = pEraseInit->SectorAddress;
          address_index < ((pEraseInit->NbSectors * FLASH_SECTOR_SIZE) + pEraseInit->SectorAddress);
          address_index += FLASH_SECTOR_SIZE)
      {
        FLASH_SectorErase(address_index);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
        /* If the erase operation is completed, disable the SER Bit */
        CLEAR_BIT(FLASH->CR, FLASH_CR_SER);
        if (status != HAL_OK)
        {
          /* In case of error, stop erase procedure and return the faulty address */
          *PageError = address_index;
          break;
        }
      }
    }
    else if(pEraseInit->TypeErase == FLASH_TYPEERASE_USERDATAERASE)
    {
      address_index = pEraseInit->UserDataAddress;

      /* Check USERLOCK Bit */
      if((__HAL_FLASH_GET_FLAG(FLASH_FLAG_USRLOCK0) != 0) || (__HAL_FLASH_GET_FLAG(FLASH_FLAG_USRLOCK1) != 0))
      {
         *PageError = address_index;
        
         /* Process Unlocked */
         __HAL_UNLOCK(&pFlash);
         return HAL_ERROR;
      }
          
      /* Wait for last operation to be completed */
      if (FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE) == HAL_OK)
      {
        /*Initialization of PageSectorError variable*/
        *PageError = 0xFFFFFFFFU;

        FLASH_UserDataErase(pEraseInit->UserDataAddress);
        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
        /* If the erase operation is completed, disable the UPER Bit */
        CLEAR_BIT(FLASH->CR, FLASH_CR_UPER);
        if (status != HAL_OK)
        {
          /* In case of error, stop erase procedure and return the faulty address */
          *PageError = address_index;
        } 
      }
    }
    else
    {
      /* nothing to do */
    }
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Perform a mass erase or erase the specified FLASH memory pages with interrupt enabled.
  * @param  pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
  *         contains the configuration information for the erasing.
  * @retval HAL_Status
  */
HAL_StatusTypeDef HAL_FLASHEx_Erase_IT(FLASH_EraseInitTypeDef *pEraseInit)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* save procedure for interrupt treatment */
  pFlash.ProcedureOnGoing = pEraseInit->TypeErase;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status != HAL_OK)
  {
    /* Process Unlocked */
    __HAL_UNLOCK(&pFlash);
  }
  else  
  {
    /* Enable End of Operation and Error interrupts */
    __HAL_FLASH_ENABLE_IT(FLASH_IT_EOP | FLASH_IT_OPERR);
  
    if(pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)
    {
      /* Set Bank to Bank for Interrupt callback management */
      pFlash.Bank = pEraseInit->Banks;

      /* Mass erase to be done */
      FLASH_MassErase(pEraseInit->Banks);
    }
    else if(pEraseInit->TypeErase == FLASH_TYPEERASE_PAGEERASE)
    {
      /* Erase by page to be done */
      pFlash.NbPagesSectorsToErase = pEraseInit->NbPages;
      pFlash.PageOrSector = pEraseInit->PageAddress;

      /*Erase 1st page and wait for IT */
      FLASH_PageErase(pEraseInit->PageAddress);
    }
    else if(pEraseInit->TypeErase == FLASH_TYPEERASE_SECTORERASE)
    {
      /* Erase by sector to be done */
      pFlash.NbPagesSectorsToErase = pEraseInit->NbSectors;
      pFlash.PageOrSector = pEraseInit->SectorAddress;
        
      FLASH_SectorErase(pEraseInit->SectorAddress);
    }
    else if (pEraseInit->TypeErase == FLASH_TYPEERASE_USERDATAERASE)
    {
      /* Set Page to UserDataAddress for Interrupt callback management */
      pFlash.PageOrSector = pEraseInit->UserDataAddress;
     
      FLASH_UserDataErase(pEraseInit->UserDataAddress);
    }
    else
    {
      /* nothing to do */
    }
  }
  return status;
}

/**
  * @brief  Program Option bytes.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that
  *         contains the configuration information for the programming.
  * @retval HAL_Status
  */
HAL_StatusTypeDef HAL_FLASHEx_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit)
{
  uint32_t optr = 0;
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the parameters */
  assert_param(IS_OPTIONBYTE(pOBInit->OptionType));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* Write protection configuration */
  if ((pOBInit->OptionType & OPTIONBYTE_WRP) != 0U)
  {
    /* Configure of Write protection on the selected area */
    if (FLASH_OB_WRPConfig(pOBInit->WRPSector) != HAL_OK)
    {
      status = HAL_ERROR;
    }
  }

  if((pOBInit->OptionType & (OPTIONBYTE_RDP | OPTIONBYTE_USER)) == (OPTIONBYTE_RDP | OPTIONBYTE_USER))
  {
    /* Configure the user option bytes */
    FLASH_OB_OptrConfig(pOBInit->USERType, pOBInit->USERConfig, pOBInit->RDPLevel);
  }
  else if ((pOBInit->OptionType & OPTIONBYTE_RDP) != 0U)
  {
    /* Only modify RDP so get current user data */
    optr = FLASH_OB_GetUser();
    /* Configure the Read protection level */
    FLASH_OB_OptrConfig(optr, optr, pOBInit->RDPLevel);
  }
  else if ((pOBInit->OptionType & OPTIONBYTE_USER) != 0U)
  {
    /* Only modify user so get current RDP level */
    optr = FLASH_OB_GetRDP();
    /* Configure the user option bytes */
    FLASH_OB_OptrConfig(pOBInit->USERType, pOBInit->USERConfig, optr);
  }
  else
  {
    /* nothing to do */
  }
  /* PCROP Configuration */
  if ((pOBInit->OptionType & OPTIONBYTE_PCROP) != 0U)
  {
    if (pOBInit->PCROPStartAddr != pOBInit->PCROPEndAddr)
    {
      /* Configure the Proprietary code readout protection */
      if (FLASH_OB_PCROPConfig(pOBInit->PCROPConfig, pOBInit->PCROPStartAddr, pOBInit->PCROPEndAddr) != HAL_OK)
      {
        status = HAL_ERROR;
      }
    }
  }
  /* SECR Configuration */
  if ((pOBInit->OptionType & OPTIONBYTE_SEC) != 0U)
  {
    /* Configure the Proprietary code readout protection */
    if (FLASH_OB_SecMemConfig(pOBInit->SECConfig, pOBInit->SecSize) != HAL_OK)
    {
      status = HAL_ERROR;
    }
  }

  /* starts to modify Flash Option bytes */
  FLASH->CR |= FLASH_CR_OPTSTRT;

  /* set bit EOPIE */
  FLASH->CR |= FLASH_CR_EOPIE;
 
  /* trigger program */
  *((__IO uint32_t *)(0x1FFF1B00)) = 0xffffffff;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Get the Option bytes configuration.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that contains the
  *         configuration information.
  * @note   The fields pOBInit->WRPBlock and pOBInit->PCROPConfig should indicate
  *         which area is requested for the WRP and PCROP, else no information will be returned.
  * @retval None
  */
void HAL_FLASHEx_OBGetConfig(FLASH_OBProgramInitTypeDef *pOBInit)
{
  pOBInit->OptionType = (OPTIONBYTE_WRP | OPTIONBYTE_RDP | OPTIONBYTE_USER);

  /* Get Write protection level */
  pOBInit->WRPSector = FLASH_OB_GetWRP();

  /* Get Read protection level */
  pOBInit->RDPLevel = FLASH_OB_GetRDP();

  /* Get the user option bytes */
  pOBInit->USERConfig = FLASH_OB_GetUser();

  if ((pOBInit->PCROPConfig == FLASH_BANK_0) || (pOBInit->PCROPConfig == FLASH_BANK_1))
  {
    pOBInit->OptionType |= OPTIONBYTE_PCROP;
    /* Get the Proprietary code readout protection */
    FLASH_OB_GetPCROP(&(pOBInit->PCROPConfig), &(pOBInit->PCROPStartAddr), &(pOBInit->PCROPEndAddr));
  }
  else
  {
    /* Nothing to do */
  }
  if ((pOBInit->SECConfig == FLASH_BANK_0) || (pOBInit->SECConfig == FLASH_BANK_1))
  {
    pOBInit->OptionType |= OPTIONBYTE_SEC;
    /* Get the Proprietary code readout protection */
    FLASH_OB_GetSecMem(&(pOBInit->SECConfig), &(pOBInit->SecSize));
  }
  else
  {
    /* Nothing to do */
  }
}

/**
  * @brief  Enable the FLASH Securable Memory protection.
  * @param  SecBank specifies the configuration (Bank to be configured).
  *         This parameter must be a one of FLASH_BANK_0 or FLASH_BANK_1.
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_EnableSecMemProtection(uint32_t SecBank)
{
  HAL_StatusTypeDef status = HAL_OK;

  if(SecBank == FLASH_BANK_0)
  {
    SET_BIT(FLASH->CR, FLASH_CR_SECPROT0);
  }
  else if(SecBank == FLASH_BANK_1)
  {
    SET_BIT(FLASH->CR, FLASH_CR_SECPROT1);
  }
  else
  {
    status = HAL_ERROR;
  }

  return status;
}

/**
  * @}
  */

/**
  * @}
  */

/* Private functions ---------------------------------------------------------*/

/** @addtogroup FLASHEx_Private_Functions
  * @{
  */
/**
  * @brief  Mass erase of FLASH memory.
  * @param  Banks Banks to be erased.
  *         This parameter can be one of the following values:
  *            @arg FLASH_BANK_0: Bank0 to be erased
  *            @arg FLASH_BANK_1: Bank1 to be erased
  * @retval None
  */
void FLASH_MassErase(uint32_t Banks)
{
  /* Check the parameters */
  assert_param(IS_FLASH_BANK(Banks));

  SET_BIT(FLASH->CR, FLASH_CR_MER);
  
  /* Set the Mass Erase Bit for the bank if requested */
  if(READ_BIT(FLASH->OPTBFB, FLASH_OPTBFB_BFB) == 0)
  {
    if(Banks == FLASH_BANK_0)
    {
      *(__IO uint32_t *)(FLASH_BASE) = 0x12344321;
    }
    else
    {
      *(__IO uint32_t *)(FLASH_BASE + (FLASH_SIZE >> 1)) = 0x12344321;
    }
  }
  else
  {
    if(Banks == FLASH_BANK_0)
    {
      *(__IO uint32_t *)(FLASH_BASE + (FLASH_SIZE >> 1)) = 0x12344321;
    }
    else
    {
      *(__IO uint32_t *)(FLASH_BASE) = 0x12344321;
    }
  }
}

/**
  * @brief  Erase the specified FLASH memory page.
  * @param  PageAddress FLASH page to erase.
  *         This parameter must be a value between 0 and (max number of pages in the bank - 1).
  * @retval None
  */
void FLASH_PageErase(uint32_t PageAddress)
{
  /* Proceed to erase the page */
  SET_BIT(FLASH->CR, FLASH_CR_PER);
  *(__IO uint32_t *)(PageAddress) = 0x12344321;
}

/**
  * @brief  Sector erase of FLASH memory
  * @param  SectorAddress sector erase address
  * @retval None
  */
void FLASH_SectorErase(uint32_t SectorAddress)
{
  SET_BIT(FLASH->CR, FLASH_CR_SER);
  *(__IO uint32_t *)(SectorAddress) = 0x12344321;
}

/**
  * @brief  Page program of FLASH memory
  * @param  Address  Specifies the address to be programmed.
  * @param  DataAddress  Specifies the data to be programmed
  * @retval None
  */
void FLASH_Program_Page(uint32_t Address, uint32_t * DataAddress)
{
  uint8_t index=0;
  uint32_t dest = Address;
  uint32_t * src = DataAddress;
  uint32_t primask_bit;

  SET_BIT(FLASH->CR, FLASH_CR_PG);
  /* Enter critical section */
  primask_bit = __get_PRIMASK();
  __disable_irq();
  /* 32 words*/
  while(index < 64U) 
  {
    *(uint32_t *)dest = *src;
    src += 1U;
    dest += 4U;
    index++;
    if(index == 63U)
    {
      SET_BIT(FLASH->CR, FLASH_CR_PGSTRT);
    }
  }

  /* Exit critical section: restore previous priority mask */
  __set_PRIMASK(primask_bit);
}

/**
  * @brief  UserData erase 
  * @retval None
  */
void FLASH_UserDataErase(uint32_t UserDataAddress)
{
  SET_BIT(FLASH->CR, FLASH_CR_UPER);
  *(__IO uint32_t *)(UserDataAddress) = 0x12344321;
}

/**
  * @brief  Program the UserData
  * @retval None
  */
void FLASH_Program_UserData(uint32_t Address, uint32_t * DataAddress)
{
  uint8_t index=0;
  uint32_t dest = Address;
  uint32_t * src = DataAddress;
  uint32_t primask_bit;

  SET_BIT(FLASH->CR, FLASH_CR_UPG);
  /* Enter critical section */
  primask_bit = __get_PRIMASK();
  __disable_irq();
  /* 64 words*/
  while(index < 64U)
  {
    *(uint32_t *)dest = *src;
    src += 1U;
    dest += 4U;
    index++;
    if(index == 63)
    {
      SET_BIT(FLASH->CR, FLASH_CR_UPGSTRT);
    }
  }

  /* Exit critical section: restore previous priority mask */
  __set_PRIMASK(primask_bit);
}

/**
  * @brief  Configure the write protection area into Option Bytes.
  * @param  WRPArea specifies the area to be configured.
  *         This parameter can be a combination of the following values:
  *            @arg OB_WRP_BANK0_SECTOR_0         : Flash Bank 0 Sector 0
  *            @arg OB_WRP_BANK0_SECTOR_1         : Flash Bank 0 Sector 1
  *            @arg OB_WRP_BANK0_SECTOR_2         : Flash Bank 0 Sector 2
  *            @arg OB_WRP_BANK0_SECTOR_3         : Flash Bank 0 Sector 3
  *            @arg OB_WRP_BANK0_SECTOR_4         : Flash Bank 0 Sector 4
  *            @arg OB_WRP_BANK0_SECTOR_5         : Flash Bank 0 Sector 5
  *            @arg OB_WRP_BANK0_SECTOR_6         : Flash Bank 0 Sector 6
  *            @arg OB_WRP_BANK0_SECTOR_7         : Flash Bank 0 Sector 7
  *            @arg OB_WRP_BANK0_SECTOR_8         : Flash Bank 0 Sector 8
  *            @arg OB_WRP_BANK0_SECTOR_9         : Flash Bank 0 Sector 9
  *            @arg OB_WRP_BANK0_SECTOR_10        : Flash Bank 0 Sector 10
  *            @arg OB_WRP_BANK0_SECTOR_11        : Flash Bank 0 Sector 11
  *            @arg OB_WRP_BANK0_SECTOR_12        : Flash Bank 0 Sector 12
  *            @arg OB_WRP_BANK0_SECTOR_13        : Flash Bank 0 Sector 13
  *            @arg OB_WRP_BANK0_SECTOR_14        : Flash Bank 0 Sector 14
  *            @arg OB_WRP_BANK0_SECTOR_15        : Flash Bank 0 Sector 15
  *            @arg OB_WRP_BANK1_SECTOR_0         : Flash Bank 1 Sector 0
  *            @arg OB_WRP_BANK1_SECTOR_1         : Flash Bank 1 Sector 1
  *            @arg OB_WRP_BANK1_SECTOR_2         : Flash Bank 1 Sector 2
  *            @arg OB_WRP_BANK1_SECTOR_3         : Flash Bank 1 Sector 3
  *            @arg OB_WRP_BANK1_SECTOR_4         : Flash Bank 1 Sector 4
  *            @arg OB_WRP_BANK1_SECTOR_5         : Flash Bank 1 Sector 5
  *            @arg OB_WRP_BANK1_SECTOR_6         : Flash Bank 1 Sector 6
  *            @arg OB_WRP_BANK1_SECTOR_7         : Flash Bank 1 Sector 7
  *            @arg OB_WRP_BANK1_SECTOR_8         : Flash Bank 1 Sector 8
  *            @arg OB_WRP_BANK1_SECTOR_9         : Flash Bank 1 Sector 9
  *            @arg OB_WRP_BANK1_SECTOR_10        : Flash Bank 1 Sector 10
  *            @arg OB_WRP_BANK1_SECTOR_11        : Flash Bank 1 Sector 11
  *            @arg OB_WRP_BANK1_SECTOR_12        : Flash Bank 1 Sector 12
  *            @arg OB_WRP_BANK1_SECTOR_13        : Flash Bank 1 Sector 13
  *            @arg OB_WRP_BANK1_SECTOR_14        : Flash Bank 1 Sector 14
  *            @arg OB_WRP_BANK1_SECTOR_15        : Flash Bank 1 Sector 15
  *            @arg OB_WRP_BANK0_Pages0to31       : Flash BANK 0 Sector 0
  *            @arg OB_WRP_BANK0_Pages32to63      : Flash BANK 0 Sector 1
  *            @arg OB_WRP_BANK0_Pages64to95      : Flash BANK 0 Sector 2
  *            @arg OB_WRP_BANK0_Pages96to127     : Flash BANK 0 Sector 3
  *            @arg OB_WRP_BANK0_Pages128to159    : Flash BANK 0 Sector 4
  *            @arg OB_WRP_BANK0_Pages160to191    : Flash BANK 0 Sector 5
  *            @arg OB_WRP_BANK0_Pages192to223    : Flash BANK 0 Sector 6
  *            @arg OB_WRP_BANK0_Pages224to255    : Flash BANK 0 Sector 7
  *            @arg OB_WRP_BANK0_Pages256to287    : Flash BANK 0 Sector 8
  *            @arg OB_WRP_BANK0_Pages288to319    : Flash BANK 0 Sector 9
  *            @arg OB_WRP_BANK0_Pages320to351    : Flash BANK 0 Sector 10
  *            @arg OB_WRP_BANK0_Pages352to383    : Flash BANK 0 Sector 11
  *            @arg OB_WRP_BANK0_Pages384to415    : Flash BANK 0 Sector 12
  *            @arg OB_WRP_BANK0_Pages416to447    : Flash BANK 0 Sector 13
  *            @arg OB_WRP_BANK0_Pages448to479    : Flash BANK 0 Sector 14
  *            @arg OB_WRP_BANK0_Pages480to511    : Flash BANK 0 Sector 15
  *            @arg OB_WRP_BANK1_Pages0to31       : Flash BANK 1 Sector 0
  *            @arg OB_WRP_BANK1_Pages32to63      : Flash BANK 1 Sector 1
  *            @arg OB_WRP_BANK1_Pages64to95      : Flash BANK 1 Sector 2
  *            @arg OB_WRP_BANK1_Pages96to127     : Flash BANK 1 Sector 3
  *            @arg OB_WRP_BANK1_Pages128to159    : Flash BANK 1 Sector 4
  *            @arg OB_WRP_BANK1_Pages160to191    : Flash BANK 1 Sector 5
  *            @arg OB_WRP_BANK1_Pages192to223    : Flash BANK 1 Sector 6
  *            @arg OB_WRP_BANK1_Pages224to255    : Flash BANK 1 Sector 7
  *            @arg OB_WRP_BANK1_Pages256to287    : Flash BANK 1 Sector 8
  *            @arg OB_WRP_BANK1_Pages288to319    : Flash BANK 1 Sector 9
  *            @arg OB_WRP_BANK1_Pages320to351    : Flash BANK 1 Sector 10
  *            @arg OB_WRP_BANK1_Pages352to383    : Flash BANK 1 Sector 11
  *            @arg OB_WRP_BANK1_Pages384to415    : Flash BANK 1 Sector 12
  *            @arg OB_WRP_BANK1_Pages416to447    : Flash BANK 1 Sector 13
  *            @arg OB_WRP_BANK1_Pages448to479    : Flash BANK 1 Sector 14
  *            @arg OB_WRP_BANK1_Pages480to511    : Flash BANK 1 Sector 15
  * @retval HAL_Status
  */
static HAL_StatusTypeDef FLASH_OB_WRPConfig(uint32_t WRPArea)
{
  HAL_StatusTypeDef status;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Configure the BANK0 WRP area in the option bytes register */
    MODIFY_REG(FLASH->WRPR0, FLASH_WRPR0_BANK0_WRPR, (uint16_t)(~WRPArea));

    /* Configure the BANK1 WRP area in the option bytes register */
    MODIFY_REG(FLASH->WRPR1, FLASH_WRPR1_BANK1_WRPR, (uint16_t)(~(WRPArea >> 16)));
  }

  return status;
}

/**
  * @brief  Set the read protection level into Option Bytes.
  * @param  RDPLevel specifies the read protection level.
  *         This parameter can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_1: Memory Read protection
  *
  * @retval HAL_Status
  */
HAL_StatusTypeDef HAL_FLASH_OB_RDPConfig(uint32_t RDPLevel)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_OB_RDP_LEVEL(RDPLevel));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Process Locked */
    __HAL_LOCK(&pFlash);

    /* Clean the error context */
    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

    /* Configure the RDP level in the option bytes register */
    MODIFY_REG(FLASH->OPTR1, FLASH_OPTR1_RDP, RDPLevel);

    /* Set OPTSTRT Bit */
    SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

    /* set bit EOPIE */
    FLASH->CR |= FLASH_CR_EOPIE;

    /* trigger program */
    *((__IO uint32_t *)(0x1FFF1B00))=0xffffffff;

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

    /* Process Unlocked */
    __HAL_UNLOCK(&pFlash);
  }

  return status;
}

/**
  * @brief  Set the BFB into Option Bytes.
  * @param  BFBStart specifies the read protection level.
  *         This parameter can be one of the following values:
  *            @arg OB_BFB_START_BANK0: No protection
  *            @arg OB_BFB_START_BANK1: Memory Read protection
  *
  * @retval HAL_Status
  */
HAL_StatusTypeDef HAL_FLASHEx_OBBFBProgram(uint32_t BFBStart)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_OB_USER_BFB(BFBStart));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Process Locked */
    __HAL_LOCK(&pFlash);

    /* Clean the error context */
    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

    /* Configure the RDP level in the option bytes register */
    MODIFY_REG(FLASH->OPTBFB, FLASH_OPTBFB_BFB, BFBStart);

    /* Set OPTSTRT Bit */
    SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

    /* set bit EOPIE */
    FLASH->CR |= FLASH_CR_EOPIE;

    /* trigger program */
    *((__IO uint32_t *)(0x1FFF1C00))=0xffffffff;

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

    /* Process Unlocked */
    __HAL_UNLOCK(&pFlash);
  }

  return status;
}

/**
  * @brief  Program the FLASH User Option Bytes.
  * @param  UserType The FLASH User Option Bytes to be modified.
  *         This parameter can be a combination of @ref FLASH_OB_USER_Type.
  * @param  UserConfig The selected User Option Bytes values:
  *         This parameter can be a combination of the following values:
  *         @ref FLASH_OB_USER_WWDG_SW,
  *         @ref FLASH_OB_USER_nRST_STANDBY,
  *         @ref FLASH_OB_USER_NRST_STOP,
  *         @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_IWDG_STOP,
  *         @ref FLASH_OB_USER_BOOT_LOCK,
  *         @ref FLASH_OB_USER_nBOOT0,
  *         @ref FLASH_OB_USER_nSWBOOT0,
  *         @ref FLASH_OB_USER_NRST_MODE,
  *         @ref FLASH_OB_USER_NBOOT1,
  *         @ref FLASH_OB_USER_SWD_MODE,
  *         @ref FLASH_OB_USER_ACC_CTRL,
  *         @ref FLASH_OB_USER_PLL_UNLOCK_RSTEN,
  *         @ref FLASH_OB_USER_VERIFY_READEN
  * @param  RDPLevel The selected User RDP Level values:
  *         @arg OB_RDP_LEVEL_0: No protection
  *         @arg OB_RDP_LEVEL_1: Memory Read protection
  * @retval HAL_Status
  */
static void FLASH_OB_OptrConfig(uint32_t UserType, uint32_t UserConfig, uint32_t RDPLevel)
{
  uint32_t optr1;
  uint32_t optr2;

  /* Check the parameters */
  assert_param(IS_OB_USER_TYPE(UserType));
  assert_param(IS_OB_USER_CONFIG(UserType, UserConfig));
  assert_param(IS_OB_RDP_LEVEL(RDPLevel));

  /* Configure the RDP level in the option bytes register */
  optr1 = FLASH->OPTR1;
  optr2 = FLASH->OPTR2;
  optr1 &= ~((UserType & 0x3FFFFFF) | FLASH_OPTR1_RDP);
  optr2 &= ~((UserType >> 26) & 0x3F);
  FLASH->OPTR1 = (optr1 | UserConfig | RDPLevel);
  FLASH->OPTR2 = (optr2 | (UserConfig >> 26));
}

/**
  * @brief  Configure the Proprietary code readout protection area into Option Bytes.
  * @param  PCROPConfig specifies the configuration .
  *         This parameter must be a combination of FLASH_BANK_0 or FLASH_BANK_1 .
  * @param  PCROPStartAddr specifies the start address of the Proprietary code readout protection.
  *         This parameter can be an address between begin and end of the bank.
  * @param  PCROPEndAddr specifies the end address of the Proprietary code readout protection.
  *         This parameter can be an address between PCROPStartAddr and end of the bank.
  * @retval HAL_Status
  */
static HAL_StatusTypeDef FLASH_OB_PCROPConfig(uint32_t PCROPConfig, uint32_t PCROPStartAddr, uint32_t PCROPEndAddr)
{
  HAL_StatusTypeDef status;
  uint32_t reg_value1;
  uint32_t reg_value2;
  uint32_t bank0_addr;
  uint32_t bank1_addr;

  /* Check the parameters */
  assert_param(IS_FLASH_BANK_EXCLUSIVE(PCROPConfig & FLASH_BANK_MSK));
  assert_param(IS_FLASH_MAIN_MEM_ADDRESS(PCROPStartAddr));
  assert_param(IS_FLASH_MAIN_MEM_ADDRESS(PCROPEndAddr));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Get the information about the bank swapping */
    if (READ_BIT(FLASH->OPTBFB, FLASH_OPTBFB_BFB) == 0U)
    {
      bank0_addr = FLASH_BASE;
      bank1_addr = FLASH_BASE + (FLASH_SIZE >> 1);
    }
    else
    {
      bank0_addr = FLASH_BASE + (FLASH_SIZE >> 1);
      bank1_addr = FLASH_BASE;
    }

    /* Configure the Proprietary code readout protection */
    if ((PCROPConfig & FLASH_BANK_MSK) == FLASH_BANK_0)
    {
      /* Calculate the location of the PCROPStartAddr in FLASH */
      reg_value1 = ((PCROPStartAddr - bank0_addr) >> 8);
      reg_value2 = (((PCROPEndAddr - bank0_addr) >> 8) << FLASH_PCROPR0_PCROP0ER_Pos);

      MODIFY_REG(FLASH->PCROPR0,(FLASH_PCROPR0_PCROP0SR | FLASH_PCROPR0_PCROP0ER), (reg_value1 | reg_value2));
    }
    else if ((PCROPConfig & FLASH_BANK_MSK) == FLASH_BANK_1)
    {
      reg_value1 = ((PCROPStartAddr - bank1_addr) >> 8);
      reg_value2 = (((PCROPEndAddr - bank1_addr) >> 8) << FLASH_PCROPR1_PCROP1ER_Pos);
      
      MODIFY_REG(FLASH->PCROPR1,(FLASH_PCROPR1_PCROP1SR | FLASH_PCROPR1_PCROP1ER), (reg_value1 | reg_value2));
    }
    else
    {
      /* Nothing to do */
    }
  }

  return status;
}

/**
  * @brief  Configure the Securable memory area into Option Bytes.
  * @param  SECPROTConfig specifies the configuration.
  *         This parameter must be a combination of FLASH_BANK_0 or FLASH_BANK_1 .
  * @param  PROTSize specifies the start address of the Proprietary code readout protection.
  *         This parameter can be an address between begin and end of the bank.
  * @retval HAL_Status
  */
static HAL_StatusTypeDef FLASH_OB_SecMemConfig(uint32_t SECPROTConfig, uint32_t SecSize)
{
  HAL_StatusTypeDef status;
  uint32_t prot_size = SecSize;

  /* Check the parameters */
  assert_param(IS_FLASH_BANK_EXCLUSIVE(SECPROTConfig & FLASH_BANK_MSK));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Check Read protect level */
    if(FLASH_OB_GetRDP() == OB_RDP_LEVEL_0)
    {
      /* Get the information about the bank swapping */
      if ((SECPROTConfig & FLASH_BANK_MSK) == FLASH_BANK_0)
      {
        WRITE_REG(FLASH->SECR0, prot_size << 16);
      }
      else if((SECPROTConfig & FLASH_BANK_MSK) == FLASH_BANK_1)
      {
        WRITE_REG(FLASH->SECR1, prot_size << 16);
      }
      else
      {
        /* Nothing to do */
      }
    }
    else
    {
      /* RDP status */
    }
  }

  return status;
}

/**
  * @brief  Return the Write Protection configuration into Option Bytes.
  * @retval None
  */
static uint32_t FLASH_OB_GetWRP(void)
{
  uint32_t WrpArea = 0;
  WrpArea = (~FLASH->WRPR0) & 0xFFFF;
  WrpArea |= (((~FLASH->WRPR1) & 0xFFFF) << 16);
  return WrpArea;
}

/**
  * @brief  Return the FLASH Read Protection level into Option Bytes.
  * @retval RDP_Level
  *         This return value can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_1: Read protection of the memory
  *            @arg OB_RDP_LEVEL_2: Read protection of the memory
  */
static uint32_t FLASH_OB_GetRDP(void)
{
  uint32_t rdp_level = READ_BIT(FLASH->OPTR1, FLASH_OPTR1_RDP);

  if (rdp_level != OB_RDP_LEVEL_0)
  {
    if (rdp_level == OB_RDP_LEVEL_2)
    {
      return (OB_RDP_LEVEL_2);
    }
    else
    {
      return (OB_RDP_LEVEL_1);
    }
  }
  else
  {
    return rdp_level;
  }
}

/**
  * @brief  Return the FLASH User Option Byte value.
  * @retval OB_user_config
  *         This parameter can be a combination of the following values:
  *         @ref FLASH_OB_USER_WWDG_SW,
  *         @ref FLASH_OB_USER_nRST_STANDBY,
  *         @ref FLASH_OB_USER_NRST_STOP,
  *         @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_IWDG_STOP,
  *         @ref FLASH_OB_USER_BOOT_LOCK,
  *         @ref FLASH_OB_USER_nBOOT0,
  *         @ref FLASH_OB_USER_nSWBOOT0,
  *         @ref FLASH_OB_USER_NRST_MODE,
  *         @ref FLASH_OB_USER_NBOOT1,
  *         @ref FLASH_OB_USER_SWD_MODE,
  *         @ref FLASH_OB_USER_ACC_CTRL,
  *         @ref FLASH_OB_USER_PLL_UNLOCK_RSTEN,
  *         @ref FLASH_OB_USER_VERIFY_READEN
  */
static uint32_t FLASH_OB_GetUser(void)
{
  uint32_t UserConfig = 0;
  UserConfig  = (READ_REG(FLASH->OPTR1) & 0x3FFFFFF);
  UserConfig |= ((READ_REG(FLASH->OPTR2) & 0x3F) << 26);
  CLEAR_BIT(UserConfig, FLASH_OPTR1_RDP);

  return UserConfig;
}

/**
  * @brief  Return the FLASH PCROP configuration into Option Bytes.
  * @param[in,out] PCROPConfig specifies the configuration (Bank to be configured).
  *        This parameter must be a one of FLASH_BANK_0 or FLASH_BANK_1.
  * @param[out] PCROPStartAddr specifies the address where to copied the start address
  *        of the Proprietary code readout protection.
  * @param[out] PCROPEndAddr specifies the address where to copied the end address of
  *        the Proprietary code readout protection.
  * @retval None
  */
static void FLASH_OB_GetPCROP(uint32_t *PCROPConfig, uint32_t *PCROPStartAddr, uint32_t *PCROPEndAddr)
{
  uint32_t reg_value;
  uint32_t bank0_addr;
  uint32_t bank1_addr;

  /* Get the information about the bank swapping */
  if (READ_BIT(FLASH->OPTBFB, FLASH_OPTBFB_BFB) == 0U)
  {
    bank0_addr = FLASH_BASE;
    bank1_addr = FLASH_BASE + (FLASH_SIZE >> 1);
  }
  else
  {
    bank0_addr = FLASH_BASE + (FLASH_SIZE >> 1);
    bank1_addr = FLASH_BASE;
  }

  if (((*PCROPConfig) & FLASH_BANK_MSK) == FLASH_BANK_0)
  {
    /* Calculate PCROPStartAddr by FLASH->PCROP0SR */
    reg_value       = ((READ_REG(FLASH->PCROPR0) & FLASH_PCROPR0_PCROP0SR) << 8);
    *PCROPStartAddr = (reg_value) + bank0_addr;

    /* Calculate PCROPEndAddr by FLASH->PCROP0ER */
    reg_value     = (((READ_REG(FLASH->PCROPR0) & FLASH_PCROPR0_PCROP0ER) << 8) >> FLASH_PCROPR0_PCROP0ER_Pos);
    *PCROPEndAddr = (reg_value) + bank0_addr;
  }
  else if (((*PCROPConfig) & FLASH_BANK_MSK) == FLASH_BANK_1)
  {
    /* Calculate PCROPStartAddr by FLASH->PCROP1SR */
    reg_value       = ((READ_REG(FLASH->PCROPR1) & FLASH_PCROPR1_PCROP1SR) << 8);
    *PCROPStartAddr = (reg_value) + bank1_addr;

    /* Calculate PCROPEndAddr by FLASH->PCROP1ER */
    reg_value     = (((READ_REG(FLASH->PCROPR1) & FLASH_PCROPR1_PCROP1ER) << 8) >> FLASH_PCROPR1_PCROP1ER_Pos);
    *PCROPEndAddr = (reg_value) + bank1_addr;
  }
  else
  {
    /* Nothing to do */
  }
}

/**
  * @brief  Return the FLASH SEC configuration into Option Bytes.
  * @param[in,out] PCROPConfig specifies the configuration (Bank to be configured).
  *        This parameter must be a one of FLASH_BANK_0 or FLASH_BANK_1.
  * @param[out] SecUsePage specifies the pages where toprotection.
  * @retval None
  */
static void FLASH_OB_GetSecMem(uint32_t *SECConfig, uint32_t *SecUsePage)
{
  if (((*SECConfig) & FLASH_BANK_MSK) == FLASH_BANK_0)
  {
    /* Calculate SecUsePage by FLASH->SECR0 */
    *SecUsePage = (READ_BIT(FLASH->SECR0, FLASH_SECR0_SEC_SIZEER0) >> 16);
  }
  else if (((*SECConfig) & FLASH_BANK_MSK) == FLASH_BANK_1)
  {
    /* Calculate SecUsePage by FLASH->SECR1 */
    *SecUsePage = (READ_BIT(FLASH->SECR1, FLASH_SECR1_SEC_SIZEER1) >> 16);
  }
  else
  {
    /* Nothing to do */
  }
}

/**
  * @brief  Return the FLASH BFB into Option Bytes.
  * @retval BFB_start
  *         This return value can be one of the following values:
  *            @arg OB_BFB_START_BANK0: BFB start bank0
  *            @arg OB_BFB_START_BANK1: BFB start bank1
  */
uint32_t FLASH_OB_GetBFB(void)
{
  uint32_t bfb_start = READ_BIT(FLASH->OPTBFB, FLASH_OPTBFB_BFB);

  if (bfb_start != OB_BFB_START_BANK0)
  {
    return (OB_BFB_START_BANK1);
  }
  else
  {
    return bfb_start;
  }
}

/**
  * @}
  */

#endif /* HAL_FLASH_MODULE_ENABLED */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT Puya *****END OF FILE******************/
